package com.winit.openapi.oauth.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.fastjson.JSONObject;
import com.winit.common.query.Page;
import com.winit.common.query.Searchable;
import com.winit.common.spi.SPIException;
import com.winit.common.spi.command.GetByKeyCommand;
import com.winit.common.spi.context.CommandContext;
import com.winit.openapi.constants.ApiConstant;
import com.winit.openapi.constants.ErrorCode;
import com.winit.openapi.controller.BaseController;
import com.winit.openapi.exception.ApiBizException;
import com.winit.openapi.exception.ApiException;
import com.winit.openapi.model.RequestMsg;
import com.winit.openapi.model.ResponseMsg;
import com.winit.openapi.oauth.builder.APIOAuhtBuilder;
import com.winit.openapi.oauth.command.QueryClientByDeveloperCommand;
import com.winit.openapi.oauth.command.QueryUserAuthInfoByClientCommand;
import com.winit.openapi.oauth.manager.APIClientManager;
import com.winit.openapi.oauth.manager.APIDeveloperManager;
import com.winit.openapi.oauth.manager.UserAuthorizationManager;
import com.winit.openapi.oauth.util.OAuth2SecurityUtil;
import com.winit.openapi.oauth.validator.APIOAuth2Validator;
import com.winit.openapi.oauth.vo.APIClientVo;
import com.winit.openapi.oauth.vo.APIDeveloperVo;
import com.winit.openapi.oauth.vo.UserAuthorizationVo;
import com.winit.openapi.util.RedisUtil;
import com.winit.openapi.util.ValidateUtil;
import com.winit.ums.spi.UmsUserService;
import com.winit.ums.spi.user.command.QueryTokenCommand;
import com.winit.ums.spi.vo.user.UserVO;

@Controller
@RequestMapping(value = "/winit/oauth/developer")
public class APIDeveloperController extends BaseController {

    private Logger                   logger = LoggerFactory.getLogger(APIDeveloperController.class);

    @Resource
    private APIDeveloperManager      apiDeveloperManager;

    @Resource
    private APIClientManager         apiClientManager;

    @Resource
    private APIOAuhtBuilder          apiOAuhtBuilder;

    @Resource
    private UserAuthorizationManager userAuthorizationManager;

    @Resource
    private UmsUserService           umsUserService;

    /**
     * 开发者登录，两种方式：1.自己注册账号 2.使用WINIT账号登录
     * 
     * @param requestMsg
     * @return
     */
    @RequestMapping(value = "/login")
    public String login(@ModelAttribute RequestMsg requestMsg, @ModelAttribute ResponseMsg responseMsg) {

        Object obj = requestMsg.getData();
        if (obj instanceof JSONObject) {
            JSONObject json = (JSONObject) obj;
            ValidateUtil.validNotNull(json, "developerAccount");
            ValidateUtil.validNotNull(json, "password");
            ValidateUtil.validNotNull(json, "mode");
            String account = json.getString("developerAccount");
            String password = json.getString("password");
            String mode = json.getString("mode");
            APIDeveloperVo loginVo = null;
            Map<String, Object> data = new HashMap<String, Object>();

            try {
                if (("default").equals(mode)) {
                    loginVo = apiDeveloperManager.login(account, password);
                    if (loginVo == null) {
                        throw new ApiException(ErrorCode.OAUTH_DEVELOPER_LOGIN_FAIL);
                    }
                    data.put("developerAccount", loginVo.getDeveloperAccount());

                } else if ("winit".equals(mode)) {
                    QueryTokenCommand command = new QueryTokenCommand();
                    command.setUserName(account);
                    command.setPassword(password);
                    String token = umsUserService.getToken(command);
                    if (StringUtils.isBlank(token)) {
                        throw new ApiException(ErrorCode.OAUTH_DEVELOPER_LOGIN_FAIL);
                    }
                    GetByKeyCommand<String> getByKeyCommand = new GetByKeyCommand<String>();
                    getByKeyCommand.setSearchKey(account);
                    UserVO userVo = umsUserService.queryByUsername(getByKeyCommand);
                    if (userVo == null) {
                        throw new ApiException(ErrorCode.OAUTH_WINIT_USERNAME_NOT_EXISTS_ERROR);
                    }
                    loginVo = apiDeveloperManager.loginByWinitAccount(userVo.getId());
                    data.put("developerAccount", userVo.getUsername());

                } else {
                    throw new ApiException(ErrorCode.OAUTH_DEVELOPER_LOGIN_MODE_ERROR);
                }
                if (null != loginVo) {
                    data.put("developerId", loginVo.getId());
                }

                responseMsg.setData(data);
            } catch (SPIException e) {
                logger.error("APIDeveloperController->login developer :error:{}", e);
                throw new ApiException(ErrorCode.OAUTH_DEVELOPER_LOGIN_FAIL);
            }

        } else {
            throw new ApiException(ErrorCode.FIELD_TYPE_ERROR, "data");
        }

        return SUCCESS;
    }

    @RequestMapping(value = "/register")
    public String register(@ModelAttribute RequestMsg requestMsg, @ModelAttribute ResponseMsg responseMsg) {

        Object obj = requestMsg.getData();
        if (obj instanceof JSONObject) {
            JSONObject json = (JSONObject) obj;
            APIOAuth2Validator.validateDeveloperAdd(json);
            APIDeveloperVo vo = apiOAuhtBuilder.buildDeveloperAdd(json);
            APIDeveloperVo loginVo = null;
            try {
                if ("default".equals(json.getString("mode"))) {
                    if (apiDeveloperManager.checkExists(vo.getDeveloperAccount())) {
                        throw new ApiException(ErrorCode.OAUTH_DEVELOPER_ALREADY_EXISTS_ERROR);
                    }
                } else if ("winit".equals(json.getString("mode"))) {
                    loginVo = apiDeveloperManager.loginByWinitAccount(vo.getWinitUserId());
                    if (loginVo != null) {
                        throw new ApiException(ErrorCode.OAUTH_DEVELOPER_ALREADY_EXISTS_ERROR);
                    }
                }
                apiDeveloperManager.insertDeveloper(vo);
                loginVo = apiDeveloperManager.selectByAccount(vo.getDeveloperAccount());
                if (loginVo == null) {
                    throw new ApiException(ErrorCode.OAUTH_DEVELOPER_NOT_EXISTS_ERROR);
                }
                Map<String, Object> data = new HashMap<String, Object>();
                data.put("developerAccount", loginVo.getDeveloperAccount());
                if ("winit".equals(json.getString("mode"))) {
                    String winitUserName = json.getString("developerAccount");
                    data.put("developerAccount", winitUserName);
                }
                data.put("developerId", loginVo.getId());
                responseMsg.setData(data);
            } catch (ApiBizException e) {
                logger.error("APIDeveloperController->create developer :error:{}", e);
                throw new ApiException(ErrorCode.OAUTH_DEVELOPER_CREATE_ERROR);
            }

        } else {
            throw new ApiException(ErrorCode.FIELD_TYPE_ERROR, "data");
        }

        return SUCCESS;
    }

    @RequestMapping(value = "/update")
    public String update(@ModelAttribute RequestMsg requestMsg) {

        Object obj = requestMsg.getData();
        if (obj instanceof JSONObject) {
            JSONObject json = (JSONObject) obj;
            APIOAuth2Validator.validateDeveloperUpdate(json);
            try {
                APIDeveloperVo vo = apiDeveloperManager.queryById(json.getLongValue("id"));
                if (null == vo) {
                    throw new ApiException(ErrorCode.OAUTH_DEVELOPER_NOT_EXISTS_ERROR,
                        json.getString("developerAccount"));
                }

                String type = json.getString("type");
                if (!vo.getDeveloperType().equals(type.trim())) {
                    throw new ApiException(ErrorCode.OAUTH_DEVELOPER_ATTR_NOT_SUPPORT_UPDATE_ERROR, "type");
                }
                String developerAccount = json.getString("developerAccount");
                // 说明客户进行了developerAccount的修改,需要校验名称的重复性
                if (!vo.getDeveloperAccount().equals(developerAccount.trim())) {
                    if (apiDeveloperManager.checkExists(developerAccount)) {
                        throw new ApiException(ErrorCode.OAUTH_DEVELOPER_ALREADY_EXISTS_ERROR);
                    }
                    vo.setDeveloperAccount(developerAccount);
                }
                apiOAuhtBuilder.buildDeveloperUpdate(json, vo);

                apiDeveloperManager.updateDeveloper(vo);
            } catch (ApiBizException e) {
                logger.error("APIDeveloperController->update developer :error:{}", e);
                throw new ApiException(ErrorCode.OAUTH_DEVELOPER_UPDATE_ERROR);
            }

        } else {
            throw new ApiException(ErrorCode.FIELD_TYPE_ERROR, "data");
        }

        return SUCCESS;
    }

    @RequestMapping(value = "/resetPassword")
    public String resetPassword(@ModelAttribute RequestMsg requestMsg) {
        Object obj = requestMsg.getData();
        if (obj instanceof JSONObject) {
            JSONObject json = (JSONObject) obj;
            verification(requestMsg);
            ValidateUtil.validNotNull(json, "code");
            ValidateUtil.validMaxLength(json, "code", 10, true);
            ValidateUtil.validNotNull(json, "password");
            ValidateUtil.validMaxLength(json, "password", 10, true);
            String newPassword = json.getString("password");
            APIOAuth2Validator.validDeveloperPassword(newPassword);
            String developerAccount = json.getString("developerAccount");
            String code = json.getString("code");

            String key = null;
            if (json.containsKey("mobilePhone")) {
                key = json.getString("mobilePhone");
            } else if (json.containsKey("email")) {
                key = json.getString("email");
            }
            String codeCache = RedisUtil.get(key);
            if (StringUtils.isBlank(codeCache) || !code.equals(codeCache)) {
                throw new ApiException(ErrorCode.OAUTH_VERIFICATION_CODE_ERROR);
            }

            APIDeveloperVo vo = apiDeveloperManager.selectByAccount(developerAccount);
            if (null == vo) {
                throw new ApiException(ErrorCode.OAUTH_DEVELOPER_NOT_EXISTS_ERROR, json.getString("developerAccount"));
            }
            vo.setDeveloperPassword(OAuth2SecurityUtil.md5AndSha256(newPassword, OAuth2SecurityUtil.md5(newPassword)));

            vo.setUpdatedby(developerAccount);
            vo.setUpdated(new Date());
            apiDeveloperManager.updateDeveloper(vo);

        } else {
            throw new ApiException(ErrorCode.FIELD_TYPE_ERROR, "data");
        }
        return SUCCESS;
    }

    @RequestMapping(value = "/verification")
    public String verification(@ModelAttribute RequestMsg requestMsg) {
        Object obj = requestMsg.getData();
        if (obj instanceof JSONObject) {
            JSONObject json = (JSONObject) obj;
            ValidateUtil.validNotNull(json, "developerAccount");
            ValidateUtil.validMaxLength(json, "developerAccount", 100, true);
            String developerAccount = json.getString("developerAccount");

            APIDeveloperVo vo = apiDeveloperManager.selectByAccount(developerAccount);
            if (null == vo) {
                throw new ApiException(ErrorCode.OAUTH_DEVELOPER_NOT_EXISTS_ERROR, json.getString("developerAccount"));
            }

            if (json.containsKey("mobilePhone")) {
                ValidateUtil.validMaxLength(json, "developerAccount", 30, false);
                String mobilePhone = json.getString("mobilePhone").trim();
                if (!vo.getContactPersonMobile().equals(mobilePhone)) {
                    throw new ApiException(ErrorCode.OAUTH_DEVELOPER_VERIFICATION_ERROR, mobilePhone);
                }
            } else if (json.containsKey("email")) {
                ValidateUtil.validMaxLength(json, "email", 100, false);
                String email = json.getString("email").trim();
                if (!vo.getEmail().equals(email)) {
                    throw new ApiException(ErrorCode.OAUTH_DEVELOPER_VERIFICATION_ERROR, email);
                }
            } else {
                throw new ApiException(ErrorCode.FIELD_NOT_NULL, "email or mobilePhone");
            }

        } else {
            throw new ApiException(ErrorCode.FIELD_TYPE_ERROR, "data");
        }
        return SUCCESS;
    }

    @RequestMapping(value = "/queryDeveloperDetail")
    public String queryDeveloperDetail(@ModelAttribute RequestMsg requestMsg, @ModelAttribute ResponseMsg responseMsg) {
        Object obj = requestMsg.getData();
        if (obj instanceof JSONObject) {
            JSONObject json = (JSONObject) obj;
            ValidateUtil.validNotNull(json, "developerAccount");
            ValidateUtil.validMaxLength(json, "developerAccount", 100, true);
            try {
                APIDeveloperVo vo = apiDeveloperManager.selectByAccount(json.getString("developerAccount"));
                if (null == vo) {
                    throw new ApiException(ErrorCode.OAUTH_DEVELOPER_NOT_EXISTS_ERROR,
                        json.getString("developerAccount"));
                }
                List<APIClientVo> clientList = apiClientManager.getByDeveloperAccount(json.getString("developerAccount")
                    .trim());
                Map<String, Object> data = new HashMap<String, Object>();
                data.put("clientList", apiOAuhtBuilder.buildClientList(clientList));
                data.put("platformName", vo.getPlatformName());
                data.put("developerAccount", json.getString("developerAccount"));
                data.put("type", vo.getDeveloperType());
                data.put("contactPersonName", vo.getContactPersonName());
                data.put("contactPersonMobile", vo.getContactPersonMobile());
                data.put("email", vo.getEmail());
                responseMsg.setData(data);
            } catch (ApiBizException e) {
                logger.error("APIDeveloperController->queryByDeveloper :error:{}", e);
                throw new ApiException(ErrorCode.OAUTH_DEVELOPER_QUERY_ERROR);
            }
        } else {
            throw new ApiException(ErrorCode.FIELD_TYPE_ERROR, "data");
        }
        return SUCCESS;
    }

    @RequestMapping(value = "/queryClientListByDeveloper")
    public String queryPageClientByDeveloper(@ModelAttribute RequestMsg requestMsg,
                                             @ModelAttribute ResponseMsg responseMsg) {
        Object obj = requestMsg.getData();
        if (obj instanceof JSONObject) {
            JSONObject json = (JSONObject) obj;
            ValidateUtil.validNotNull(json, "developerAccount");
            ValidateUtil.validMaxLength(json, "developerAccount", 100, true);
            APIDeveloperVo vo = apiDeveloperManager.selectByAccount(json.getString("developerAccount"));
            if (null == vo) {
                throw new ApiException(ErrorCode.OAUTH_DEVELOPER_NOT_EXISTS_ERROR, json.getString("developerAccount"));
            }
            QueryClientByDeveloperCommand command = new QueryClientByDeveloperCommand();
            command.setCtx(CommandContext.getContext());
            command.setDeveloperAccount(vo.getDeveloperAccount());
            command.setDeveloperId(vo.getId());
            Searchable searchable = buildSearchablePageParams(json);
            command.setSearchable(searchable);

            Page<APIClientVo> list = apiClientManager.findByDeveloper(command);
            Map<String, Object> data = new HashMap<String, Object>();
            int pageNo = json.getIntValue("pageNo") == 0 ? ApiConstant.PAGE_NO_DEFAULT : json.getIntValue("pageNo");
            int pageSize = json.getIntValue("pageSize") == 0 ? ApiConstant.PAGE_SIZE_DEFAULT : json.getIntValue("pageSize");

            data.put("pageNo", pageNo);
            data.put("pageSize", pageSize);
            data.put("total", list.total);

            data.put("list", apiOAuhtBuilder.buildClientList(list.content));
            responseMsg.setData(data);

        } else {
            throw new ApiException(ErrorCode.FIELD_TYPE_ERROR, "data");
        }
        return SUCCESS;
    }

    @RequestMapping(value = "/queryUserListByDeveloper")
    public String queryUserListByDeveloper(@ModelAttribute RequestMsg requestMsg,
                                           @ModelAttribute ResponseMsg responseMsg) {
        Object obj = requestMsg.getData();
        if (obj instanceof JSONObject) {
            JSONObject json = (JSONObject) obj;
            ValidateUtil.validNotNull(json, "developerAccount");
            ValidateUtil.validMaxLength(json, "developerAccount", 100, true);
            APIDeveloperVo vo = apiDeveloperManager.selectByAccount(json.getString("developerAccount"));
            if (null == vo) {
                throw new ApiException(ErrorCode.OAUTH_DEVELOPER_NOT_EXISTS_ERROR, json.getString("developerAccount"));
            }
            QueryUserAuthInfoByClientCommand command = new QueryUserAuthInfoByClientCommand();
            command.setCtx(CommandContext.getContext());
            Searchable searchable = buildSearchablePageParams(json);
            command.setSearchable(searchable);

            List<APIClientVo> list = apiClientManager.getByDeveloperAccount(vo.getDeveloperAccount());
            Map<String, Object> data = new HashMap<String, Object>();
            int pageNo = json.getIntValue("pageNo") == 0 ? ApiConstant.PAGE_NO_DEFAULT : json.getIntValue("pageNo");
            int pageSize = json.getIntValue("pageSize") == 0 ? ApiConstant.PAGE_SIZE_DEFAULT : json.getIntValue("pageSize");
            data.put("pageNo", pageNo);
            data.put("pageSize", pageSize);
            data.put("total", 0);
            if (com.alibaba.dubbo.common.utils.CollectionUtils.isNotEmpty(list)) {
                List<Long> idList = new ArrayList<Long>();
                for (APIClientVo apiClientVo : list) {
                    idList.add(apiClientVo.getId());
                }
                command.setClientIdList(idList);

                Page<UserAuthorizationVo> pageList = userAuthorizationManager.findPageByClientId(command);
                if (CollectionUtils.isNotEmpty(pageList.content)) {
                    List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();
                    for (UserAuthorizationVo authInfoVo : pageList.content) {
                        listMap.add(apiOAuhtBuilder.buildSingleUserAuthResult(authInfoVo,
                            getClientByUserAuth(authInfoVo, list)));
                    }
                    data.put("list", listMap);
                    data.put("total", pageList.total);
                }

            }
            responseMsg.setData(data);

        } else {
            throw new ApiException(ErrorCode.FIELD_TYPE_ERROR, "data");
        }
        return SUCCESS;
    }

    private APIClientVo getClientByUserAuth(UserAuthorizationVo userVo, List<APIClientVo> list) {
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        for (APIClientVo apiClientVo : list) {
            if (userVo.getClientId().longValue() == apiClientVo.getId().longValue()) {
                return apiClientVo;
            }
        }

        return null;

    }
}
